/**
 * BearerHandler.java $version 1.0 2010. 08. 12
 *
 * Copyright 2010 NHN Corp. All rights Reserved.
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.eltonkent.dashboard.service.helper;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiManager;
import android.util.Log;

import com.eltonkent.dashboard.service.ServiceAPIConstants.Bearer;

/**
 * 
 * Description:
 * 
 * The BearerHandler class holds the responsibility of initializing the
 * BearerHandler components. This class used singleton implementation logic,
 * which allows only one instance of class for whole application. The purpose of
 * the BearerHandler class maintaining the available networks and current
 * networks and register the receiver dynamically
 * 
 * @author Accenture India
 * 
 */
public final class BearerHelper {

	/**
	 * Holds the BearerHandler object instance for accessing the bearer Handler
	 * API
	 */
	private static BearerHelper bearerHandler;

	/**
	 * The BearerHandler is the primary access point to initialize bearer
	 * handler. Only static access is followed, since only one instance of the
	 * service handler will be created
	 * 
	 * @param context
	 *            application context
	 * 
	 * @return return shared instance of {@link BearerHelper}
	 */
	public static synchronized BearerHelper getInstance(Context context) {

		try {
			if (bearerHandler == null) {
				bearerHandler = new BearerHelper();
				bearerHandler.init(context);
				return bearerHandler;
			}
			return bearerHandler;
		} catch (Exception e) {
			Log.d("DBA", "Exception in BearerManager " + e);
			bearerHandler = null;
			return null;
		}
	}

	/**
	 * Holds the availableBearers
	 */
	private Set<Bearer> availableBearer;
	/**
	 * Holds the current bearer
	 */
	private Bearer currentBearer;
	/**
	 * Represents the connection manager for context
	 */
	private ConnectivityManager connectivityManager;

	/**
	 * Represents the application context
	 */
	private Context context;

	/**
	 * The BroadcastReceiver is registered by BearerHandler as a broadcast
	 * receiver to be invoked by the system when the network changes was happen
	 * like wifi state changed ,supplicant state, background setting changed and
	 * network changed are available. NetworkChangeRecevier gets the callback
	 * via onReceive().
	 */
	private BroadcastReceiver networkChangeRecevier = new BroadcastReceiver() {

		/**
		 * <p>
		 * If no connectivity is available then this receiver will update the
		 * status in bearer handler or if any network is changed it will update
		 * the status in bearer handler based on the available networks
		 * </p>
		 * 
		 * <p>
		 * It gets the new wifi state from the intent and check if wifi is
		 * disabled or unknown state ,if its disabled then its update the status
		 * in BearerHandler
		 * </p>
		 * 
		 * <p>
		 * It gets the new supplicant state and check if wifi is
		 * disconnected,its Disconnected then its update the status in
		 * BearerHandler
		 * </p>
		 */
		@Override
		public void onReceive(Context context, Intent intent) {

			final boolean noConnectivity = intent.getBooleanExtra(
					ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);

			if (noConnectivity) {
				noConnectivity();
			} else {
				final NetworkInfo networkInfo = (NetworkInfo) intent
						.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);

				if (networkInfo != null) {
					if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI
							&& networkInfo.isConnected()) {
						enableWifi();
					} else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE
							&& networkInfo.isConnected()) {
						enabled3G();
					}
				}
			}

			final int wifiState = intent.getIntExtra(
					WifiManager.EXTRA_WIFI_STATE, 0);

			if (wifiState == 0) {
				final SupplicantState supplicantState = intent
						.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);

				if (supplicantState != null
						&& supplicantState == SupplicantState.DISCONNECTED) {
					setWifiDisabled();
				}
			} else {
				if (wifiState == WifiManager.WIFI_STATE_DISABLED
						|| wifiState == WifiManager.WIFI_STATE_UNKNOWN) {
					setWifiDisabled();
				} else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
					enableWifi();
				}
			}
		}
	};

	private BearerHelper() {

	}

	/**
	 * Enable the 3g.Called from the broad cost receiver
	 * 
	 */
	public void enabled3G() {
		this.getAvailableBearer().add(Bearer.BEARER_3G);
		this.setCurrentBearer(Bearer.BEARER_3G);
	}

	/**
	 * Enable the wifi.Called from the broad cost receiver
	 * 
	 */
	public void enableWifi() {
		this.getAvailableBearer().add(Bearer.BEARER_WIFI);
		this.setCurrentBearer(Bearer.BEARER_WIFI);
	}

	/**
	 * Used to access the Available bearer
	 * 
	 */
	public Set<Bearer> getAvailableBearer() {
		return availableBearer;
	}

	/**
	 * Used to get the current bearer
	 * 
	 */
	public Bearer getCurrentBearer() {
		return currentBearer;
	}

	/**
	 * Hold the responsibility of adding the available bearer and register the
	 * broadcast receiver
	 * 
	 * @param context
	 *            application context
	 */
	private void init(Context context) {

		this.context = context;
		this.availableBearer = Collections.synchronizedSet(new HashSet<Bearer>(
				Bearer.values().length));
		connectivityManager = (ConnectivityManager) context
				.getSystemService(Context.CONNECTIVITY_SERVICE);

		if (isWifiAvailable()) {
			this.getAvailableBearer().add(Bearer.BEARER_WIFI);
			this.setCurrentBearer(Bearer.BEARER_WIFI);
		}

		if (is3GAvailable()) {
			this.getAvailableBearer().add(Bearer.BEARER_3G);
			if (this.getCurrentBearer() == null) {
				this.setCurrentBearer(Bearer.BEARER_3G);
			}
		}

		this.registerNetWorkChangeMonitorRecevier();
	}

	/**
	 * Return true if 3g is connected otherwise false
	 * 
	 * @return true 3g is connected , otherwise false
	 */
	private boolean is3GAvailable() {
		NetworkInfo networkInfo = connectivityManager
				.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);

		if ((networkInfo != null) && networkInfo.isConnected()) {
			return true;
		}
		return false;
	}

	/**
	 * Return true if wifi is connected otherwise false
	 * 
	 * @return true wifi is connected , otherwise false
	 */
	private boolean isWifiAvailable() {

		NetworkInfo networkInfo = connectivityManager
				.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

		if (networkInfo != null && networkInfo.isConnected()) {
			return true;
		}
		return false;
	}

	/**
	 * Called when the no connectivity is available
	 */
	public void noConnectivity() {
		this.getAvailableBearer().clear();
		this.setCurrentBearer(null);
	}

	/**
	 * Register the broad cost receiver if no bearer is available
	 * 
	 */
	private void registerNetWorkChangeMonitorRecevier() {
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
		intentFilter
				.addAction(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
		intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
		intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);

		this.context.registerReceiver(this.networkChangeRecevier, intentFilter);
	}

	/**
	 * Used to change the current bearer
	 * 
	 */
	private void setCurrentBearer(Bearer currentBearer) {
		this.currentBearer = currentBearer;
	}

	/**
	 * Disable the wifi connection and change current bearer into 3G if 3G is
	 * available. Called from the broad cost receiver
	 * 
	 */
	public synchronized void setWifiDisabled() {
		this.getAvailableBearer().remove(Bearer.BEARER_WIFI);

		boolean mobileAvailable = false;
		for (Bearer bearer : this.getAvailableBearer()) {
			if (bearer == Bearer.BEARER_3G) {
				mobileAvailable = true;
				break;
			}
		}

		if (mobileAvailable) {
			this.setCurrentBearer(Bearer.BEARER_3G);
		} else {
			this.setCurrentBearer(null);
		}
	}

	/**
	 * Hold the responsibility of unregister all the receiver and clean the
	 * bearer handler
	 * 
	 */
	public void shutdownBearerHandler() {
		if (context != null) {
			context.unregisterReceiver(networkChangeRecevier);
		}
		BearerHelper.bearerHandler = null;
	}
}
